#ifndef DISPLAY_PRIMITIVE_HEADER
#define DISPLAY_PRIMITIVE_HEADER

#include "Sprite.h"
#include "glm/glm.hpp"
#include "PhysicsPrimitives.h"
#include "Display.h"

enum DISPLAY_PRIMITIVE_TYPE{
	DISPLAY_PRIMITIVE = 0,
	SPRITE_DISPLAY_PRIMITIVE,
	ANIMATED_SPRITE_DISPLAY_PRIMITIVE,
	PHYSICS_DISPLAY_PRIMITIVE,
	NUM_DISPLAY_PRIMITIVE_TYPES
};

struct DisplayPrimitiveDef {
	DisplayPrimitiveDef()
		: isActive(true),
		  type(DISPLAY_PRIMITIVE),
		  doKeep(false)
	{}

	bool isActive;

	bool doKeep; //Represents whether the display manager should keep this or not.
	//XXX Use smart pointers to avoid this.

	DISPLAY_PRIMITIVE_TYPE type;

};

//An interface for everything that can be displayed.
class DisplayPrimitive {
public:
	//Creates a display primitive from a def struct.
	DisplayPrimitive( const DisplayPrimitiveDef * inDef) {}
	//Display this primitive
	virtual void Display() = 0;

	virtual bool IsActive();

	virtual void SetIsActive(bool isActive);

protected:

	bool mIsActive;
};

//Definite for creating a sprite display primitive.
struct SpriteDisplayPrimitiveDef : public DisplayPrimitiveDef {
	SpriteDisplayPrimitiveDef () 
		: DisplayPrimitiveDef(),
		  worldPos(glm::vec2(0.0, 0.0)),
		  worldWH(glm::vec2(1.0,1.0)),
		  angle(PI/2),
		  tex(NULL)
		  { type = SPRITE_DISPLAY_PRIMITIVE; }

	glm::vec2 worldPos; //Center position.
	glm::vec2 worldWH; //The W and H in world coordinates
	float angle;
	Texture * tex; //The texture used to display this sprite.

};

class SpriteDisplayPrimitive : public DisplayPrimitive {
public:

	SpriteDisplayPrimitive(const SpriteDisplayPrimitiveDef * inDef) :
	  DisplayPrimitive(static_cast<const DisplayPrimitiveDef*>(inDef)) 
	  {
		  //Set the sprite from the input definition file.
		  SetPos(inDef->worldPos);
		  SetWorldPos(inDef->worldPos);
		  SetWorldWH(inDef->worldWH);
		  SetTexture(inDef->tex);
		  SetAngle(inDef->angle);
	  }
//
//DisplayPrimitive implementations
	virtual void Display();

//
//Specific to SpriteDisplayPrimitive

	//Set center pos
	void SetPos( const glm::vec2 & inPos );

	//Get center pos
	glm::vec2 GetPos ();

	void SetWorldWH( const glm::vec2 & inWH){
		mWorldWH = inWH;

	}

	void SetAngle(float inAngle) { mAngle = inAngle; }

	float GetAngle( ) { return mAngle; }

	void SetTexture( Texture * inTex ) { mSprite.SetTexture(inTex); }

	Texture * GetTexture() { return mSprite.GetTexture(); }

	void SetWorldPos( const glm::vec2 & inPos) {
		mWorldPos = inPos;
		//XXX Move out of sprite.
		mSprite.SetPos(mWorldPos);
	}

	glm::vec2 GetWorldPos(){
		return mWorldPos;
	}

	glm::vec2 GetWorldWH() {
		return mWorldWH;
	}

	//Get sprite
	Sprite * GetSprite( );

protected:
	Sprite mSprite;

	glm::vec2 mWorldWH;
	glm::vec2 mWorldPos;

	float mAngle;
};


struct AnimatedSpriteDisplayPrimitiveDef : public SpriteDisplayPrimitiveDef {
	AnimatedSpriteDisplayPrimitiveDef() 
		: SpriteDisplayPrimitiveDef(),
		numFrames(glm::ivec2(1, 1)),
		startFrame(glm::ivec2(0, 0)),
		animatedType(ANIMATED_SPRITE_TYPE::ONE_SHOT)
	{ type = ANIMATED_SPRITE_DISPLAY_PRIMITIVE; }

	float timeToAnimate; //Time from first frame to last frame.
	ANIMATED_SPRITE_TYPE animatedType;
	glm::ivec2 numFrames;
	glm::ivec2 startFrame;

};

class AnimatedSpriteDisplayPrimitive : public SpriteDisplayPrimitive {
public:

	AnimatedSpriteDisplayPrimitive( const AnimatedSpriteDisplayPrimitiveDef * inDef)
		: SpriteDisplayPrimitive(static_cast< const SpriteDisplayPrimitiveDef *>(inDef)){
		SetPos(inDef->worldPos);
		SetWorldPos(inDef->worldPos);
		SetWorldWH(inDef->worldWH);
		SetSpriteSheetParams(inDef->numFrames.x, inDef->numFrames.y, inDef->startFrame.x, inDef->startFrame.y);
	}
	void SetSpriteSheetParams ( 
		int spanW,
		int spanH,
		int startFrameX, 
		int startFrameY);

	void NextFrame();
	void PrevFrame();

private:	

	glm::ivec2 mNumFrames; //number of frames.
	glm::ivec2 mCurrFrame;
	glm::ivec2 startFrame; //In the case of a repeat or ping-pong

	float timeToAnimate; //Time from first frame to last frame.

	ANIMATED_SPRITE_TYPE animatedType;

	glm::ivec2 numFrames;
};

struct PhysicsDisplayPrimitiveDef : public DisplayPrimitiveDef {
	PhysicsDisplayPrimitiveDef()
		: DisplayPrimitiveDef(),
		prim(NULL)
	{ type = PHYSICS_DISPLAY_PRIMITIVE; }

	PhysicsPrimitive * prim;
};

//
//Specific to PhysicalDisplayPrimitive
//Draws the physical objects
class PhysicsDisplayPrimitive : public DisplayPrimitive {
public:
	PhysicsDisplayPrimitive( const PhysicsDisplayPrimitiveDef * inDef )
		: DisplayPrimitive(inDef)
	{
		SetPrimitive(inDef->prim);
	}

	virtual void Display();

	PhysicsPrimitive * GetPrimitive();

	void SetPrimitive( PhysicsPrimitive * inPrim );

private:
	PhysicsPrimitive * mPrim;
};

DisplayPrimitive * CreateDisplayPrimitive( const DisplayPrimitiveDef * inDef );
#endif